package pitaya

import (
	gojson "encoding/json"
	"errors"
	"fmt"
	"github.com/topfreegames/pitaya/component"
	"github.com/topfreegames/pitaya/logger"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

var (
	templates = make([]templateInterface,0)
	templateMap = map[templateInterface]interface{}{}
	once      sync.Once
	typeInt64 = reflect.TypeOf(int64(0))
	typeInt = reflect.TypeOf(0)
	typeByte = reflect.TypeOf(byte(0))
	typeFloat32 = reflect.TypeOf(float32(0))
	typeFloat64 = reflect.TypeOf(float64(0))
	typeString = reflect.TypeOf("")
	typeStringArray = reflect.TypeOf([]string{})
	typeIntArray = reflect.TypeOf([]int{})
	typeFloat32Array = reflect.TypeOf([]float32{})
	valueOfNil = reflect.ValueOf(nil)
	FilterReg *regexp.Regexp
)



type (
	TemplateStruct struct {
		DescriptionMap map[string]string
		NameMap map[string]string
		TypeMap map[string]string
		data map[string]interface{}
	}

	BaseTemplate struct {
		component.Base
	}

	templateInterface interface {
		NewTemplate() interface{}
		component.Component
	}
)

func LoadAllTemplateWithPath(dir string)  {
	var err error

	nameStructMap := map[string]templateInterface{}
	for _,v := range templates {
		nameStructMap[reflect.TypeOf(v).Elem().Name()] = v
	}

	//读取文件
	fileMap := make(map[string][]byte,0)
	var files []os.FileInfo
	files,err  =  ioutil.ReadDir(dir)
	if err != nil {
		panic(err)
	}

	suffix := ".json.txt"
	for _,file := range files {
		fileName := file.Name()
		if file.IsDir() || len(fileName) <= len(suffix) || fileName[len(fileName)-len(suffix):] != suffix {
			continue
		}

		name := fileName[0:len(fileName) - len(suffix)]
		if _,ok := nameStructMap[name];!ok {
			continue
		}

		var bytes []byte
		bytes,err = ioutil.ReadFile(fmt.Sprintf("%s/%s",dir,file.Name()))
		if err != nil {
			return
		}

		fileMap[fileName[0:len(fileName) - len(suffix)]] = bytes
	}

	for name,v2 := range nameStructMap {
		bytes,ok := fileMap[name]
		if !ok {
			continue
		}

		arr := []map[string]interface{}{}
		err = gojson.Unmarshal(bytes,&arr)
		if err != nil {
			logger.Log.Errorf("[template] Init failed,error:%s",err.Error())
		}

		if len(arr) < 3 {
			continue
		}

		descriptionMap := map[string]string{}
		for k,v := range arr[0] {
			descriptionMap[k] = v.(string)
		}

		nameMap := map[string]string{}
		for k,v := range arr[1] {
			nameMap[k] = v.(string)
		}

		typeMap := map[string]string{}
		for k,v := range arr[2] {
			typeMap[k] = v.(string)
		}

		baseTempate := &TemplateStruct{
			DescriptionMap: descriptionMap,
			NameMap: nameMap,
			TypeMap: typeMap,
		}
		//解析描述
		arr = arr[3:]
		mp := map[string]interface{}{}
		for _,item := range arr {
			m := item
			if err != nil {
				logger.Log.Errorf("[template] Init failed,error:%s",err.Error())
				panic(err)
			}
			mp[m["id"].(string)] = m
		}

		baseTempate.data = mp
		err = setStructFieldByJsonName1(v2,baseTempate)
		if err != nil {
			logger.Log.Errorf("[template] Init failed,error:%s",err.Error())
		}
		templateMap[v2] = baseTempate
	}


	if err != nil {
		logger.Log.Errorf("[template] Init failed,error:%s",err.Error())
	}


	for _,v := range templates {
		v.Init()
	}

	return
}


func loadAllTemplates()  {
	LoadAllTemplateWithPath("./template/txt")
}

/**
注册模板
*/
func RegisterTemplate(key templateInterface)  {
	templates = append(templates,key)
}

func GetTemplates(key templateInterface) []templateInterface {
	v,ok := templateMap[key]
	if !ok {
		return nil
	}

	arr := make([]templateInterface,0)
	mp := v.(*TemplateStruct).data
	for k,t := range  mp {
		if k == "0" {
			continue
		}

		arr = append(arr,t.(templateInterface))
	}

	return arr
}

func GetTemplateById(key templateInterface,id string) templateInterface {
	var o bool
	var v interface{}
	if b,ok := templateMap[key];ok {
		d := b.(*TemplateStruct).data
		if d == nil {
			return nil
		}

		if v,o = d[id];o {
			return v.(templateInterface)
		}
	}

	return nil
}

func setStructFieldByJsonName1(key templateInterface,baseTemplate *TemplateStruct) error {
	for id,value := range baseTemplate.data {
		mp := value.(map[string]interface{})
		item := key.NewTemplate()
		v := reflect.ValueOf(item)
		for fieldName,v1 := range mp {
			if v1 == nil {
				continue
			}
			
			typStr,ok := baseTemplate.TypeMap[fieldName]
			if !ok {
				err := errors.New(fmt.Sprintf("fieldName:%s not exist in %s",fieldName,reflect.TypeOf(key).Elem().Name()))
				logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
				return err
			}

			if typStr == "type" || typStr == "%" {
				continue
			}

			fieldNameStr := fmt.Sprintf("%s%s",strings.ToUpper(fieldName[0:1]),fieldName[1:])
			field,exist := v.Elem().Type().FieldByName(fieldNameStr);
			if !exist {
				err := errors.New(fmt.Sprintf("fieldName:%s not exist in %s",fieldName,reflect.TypeOf(key).Elem().Name()))
				logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
				return err
			}

			if typStr == "int" {
				if fieldName == "id" {
					v.Elem().FieldByName(fieldNameStr).Set(reflect.ValueOf(v1.(string)))
					continue
				}

				if field.Type != typeInt {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s",fieldName,reflect.TypeOf(key).Elem().Name(),typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}

				i,err := strconv.ParseInt(v1.(string),10,32)
				if err != nil {
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}
				v.Elem().FieldByName(fieldNameStr).Set(reflect.ValueOf(int(i)))
			}else if typStr == "string" {
				if field.Type != typeString {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s",fieldName,reflect.TypeOf(key).Elem().Name(),typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}

				v.Elem().FieldByName(fieldNameStr).Set(reflect.ValueOf(v1.(string)))
			}else if typStr == "float" {
				if field.Type != typeFloat32 {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s",fieldName,reflect.TypeOf(key).Elem().Name(),typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}

				i,err := strconv.ParseFloat(v1.(string),32)
				if err != nil {
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}
				v.Elem().FieldByName(fieldNameStr).Set(reflect.ValueOf(float32(i)))
			}else if strings.HasPrefix(typStr,"int[") {
				if field.Type != typeIntArray {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s",fieldName,reflect.TypeOf(key).Elem().Name(),typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}

				strs := strings.Split(v1.(string),",")
				l := v.Elem().FieldByName(fieldNameStr).Len()
				for index,str := range strs {
					if len(str) == 0 {
						continue
					}

					if index >= l {
						break
					}

					i,err := strconv.ParseInt(str,10,32)
					if err != nil {
						logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
						return err
					}

					v.Elem().FieldByName(fieldNameStr).Index(index).Set(reflect.ValueOf(int(i)))
				}

			}else if strings.HasPrefix(typStr,"string[") {
				if field.Type != typeStringArray {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s", fieldName, reflect.TypeOf(key).Elem().Name(), typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s", err.Error())
					return err
				}

				l := v.Elem().FieldByName(fieldNameStr).Len()
				strs := strings.Split(v1.(string),",")
				for index,str := range strs {
					if len(str) == 0 {
						continue
					}

					if index >= l {
						break
					}

					v.Elem().FieldByName(fieldNameStr).Index(index).Set(reflect.ValueOf(str))
				}
			}else if strings.HasPrefix(typStr,"float[") {
				if field.Type != typeFloat32Array {
					err := errors.New(fmt.Sprintf("fieldName:%s type error in %s should be %s",fieldName,reflect.TypeOf(key).Elem().Name(),typStr))
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}

				l := v.Elem().FieldByName(fieldNameStr).Len()
				strs := strings.Split(v1.(string),",")
				for index,str := range strs {
					if len(str) == 0 {
						continue
					}

					if index >= l {
						break
					}

					i,err := strconv.ParseFloat(str,32)
					if err != nil {
						logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
						return err
					}

					v.Elem().FieldByName(fieldNameStr).Index(index).Set(reflect.ValueOf(float32(i)))
				}

			}else {
				err := errors.New(fmt.Sprintf("fieldName:%s,fieldType:%s not exist in %s",fieldName,typStr,reflect.TypeOf(key).Elem().Name()))
				logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
				return err
			}
		}

		baseTemplate.data[id] = item
	}

	return nil
}


//将结构体里的成员按照json名字来赋值
func setStructFieldByJsonName(key templateInterface,baseTemplate *TemplateStruct) error {
	fields := baseTemplate.data

	tempMap := make(map[int]string,0)
	for k,value := range fields {
		temp := value.(map[string]interface{})

		item := key.NewTemplate()
		v := reflect.ValueOf(item)
		for i := 0; i < v.Elem().NumField(); i++ {
			fieldInfo := v.Elem().Type().Field(i)
			name,ok := tempMap[i]
			if !ok {
				name = strings.ToLower(fieldInfo.Name[0:1]) + string([]rune(fieldInfo.Name)[1:])
				name = strings.Split(name, ",")[0]
				tempMap[i] = name
			}

			value, ok = temp[name]
			if !ok {
				return errors.New(fmt.Sprintf("加载模板结构体:%s失败，找不到字段：%s",reflect.TypeOf(key).Elem().Name(),name))
			}

			if value == nil {
				continue
			}

			switch fieldInfo.Type {
			case typeString:
				tmp := ""
				if value != nil {
					tmp = value.(string)
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(tmp))
			case typeInt64:
				tmp,err := strconv.ParseInt(value.(string),10,64)
				if err != nil {
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(tmp))
			case typeInt:
				tmp,err := strconv.ParseInt(value.(string),10,32)
				if err != nil {
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(int(tmp)))
			case typeIntArray:
				tmp := ""
				if value != nil {
					tmp = value.(string)
				}

				length := 0
				var strs []string
				if len(tmp) > 0 {
					strs = strings.Split(tmp,",")
					length = len(strs)

				}
				arr := make([]int,length)
				for j:=0;j< length;j++{
					if strings.Replace(strs[j]," ","",-1) == "" {
						continue
					}

					intValue,err := strconv.ParseInt(strs[j],10,32)
					if err != nil {
						logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
						return err
					}
					arr[j] = int(intValue)
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(arr))
			case typeFloat32:
				if value != nil {
					tmp,err := strconv.ParseFloat(value.(string),32)
					if err != nil {
						logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
						return err
					}
					v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(float32(tmp)))
				}
			case typeFloat64:
				tmp,err := strconv.ParseFloat(value.(string),64)
				if err != nil {
					logger.Log.Errorf("[template] setStructFieldByJsonName failed,error:%s",err.Error())
					return err
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(tmp))
			case typeStringArray:
				tmp := ""
				if value != nil {
					tmp = value.(string)
				}

				length := 0
				var strs []string
				if len(tmp) > 0 {
					strs = strings.Split(tmp,",")
					length = len(strs)

				}

				arr := make([]string,length)
				for j:=0;j< length;j++{
					if strings.Replace(strs[j]," ","",-1) == "" {
						continue
					}

					arr[j] = strs[j]
				}
				v.Elem().FieldByName(fieldInfo.Name).Set(reflect.ValueOf(arr))
			default:
				logger.Log.Debugf("wrong type")
			}
		}

		fields[k] = item
	}


	return nil
}

func shutdownTemplates()  {
	for _,v := range templates {
		v.Shutdown()
	}
}







